﻿#include "lua_test.h"

#include "client.h"
#include "cmd_test.h"
#include "fmt.h"
#include "login_test.h"
#include "lua/lua_timer_queue.h"  // for LuaTimerQueue

#include <LuaIntf/LuaIntf.h>

#include <iostream>

LuaTest::LuaTest(lua_State* L) :
	m_pLuaState(L),
	m_pClient(new Client),  // unique_ptr
	m_pLoginTest(new LoginTest(*m_pClient)),  // unique_ptr
	m_pTestForInner(new CmdTest(*m_pClient))
{
	assert(L);
	Client& clt = *m_pClient;
	m_thd = std::thread([&clt]() { clt.BlockingRun(); });

	InitLua();
}

LuaTest::~LuaTest()
{
	m_thd.join();
}

void LuaTest::InitLua()
{
	Bind();

	// lua脚本的顶层路径名
	LuaIntf::LuaContext ctx(m_pLuaState);
	ctx.setGlobal("G_LUA_ROOTPATH", GetLuaRootPath());
	DoLuaInitFile("common/init_common.lua");
	DoLuaInitFile("test_client/init_test_client.lua");
}

void LuaTest::PrintHelp() const
{
	std::cout << "Lua console to test server.\n"
		"Example: test.login()\n"
		"test.help() to print this information.\n";
}

static void log_(const std::string& sLogName, const std::string& sMsg)
{
	std::cout << sLogName << ": " << sMsg << std::endl;
}

void LuaTest::Bind()
{
	assert(m_pLuaState);
	assert(m_pClient);

	LuaIntf::LuaBinding(m_pLuaState).beginModule("c_logger")
		.addFunction("debug", &log_)
		.addFunction("info", &log_)
		.addFunction("warn", &log_)
		.addFunction("error", &log_)
		.addFunction("fatal", &log_)
	.endModule();

	LuaIntf::LuaBinding(m_pLuaState).beginModule("c_rpc")
		.addFunction("request", [this](
			const std::string& sService,
			const std::string& sMethod,
			const std::string& sRequest,
			const LuaIntf::LuaRef& luaCallback) {
			RequestRpc(sService, sMethod, sRequest, luaCallback);
		})
	.endModule();

	Client& clt = *m_pClient;
	LuaIntf::LuaBinding(m_pLuaState).beginModule("c_test")
		.addFunction("help", [this]() { PrintHelp(); })
		.addFunction("login", [this]() { m_pLoginTest->TestLogin(); })
		.addFunction("cmd", [this](const char *cmd) { m_pTestForInner->Test(cmd); })
	.endModule();
	
	LuaIntf::LuaBinding(m_pLuaState).beginModule("c_cmd")
		.addFunction("mongodb_test", [this]() { m_pTestForInner->Test("mongodb_test"); })
	.endModule();

	LuaTimerQueue::Bind(m_pLuaState);
}

// sLuaInitFileName 必须是相对于 LuaPath 目录的文件名，如 "base/init_base.lua"
void LuaTest::DoLuaInitFile(const std::string& sLuaInitFileName)
{
	// lua脚本的顶层路径名
	std::string sLuaInitFilePath =
		(Fmt("%1%/%2%") % GetLuaRootPath() % sLuaInitFileName).str();
	try {
		// 读取初始化文件, load所有的lua文件, 由init文件负责完成
		LuaIntf::LuaContext ctx(m_pLuaState);
		ctx.doFile(sLuaInitFilePath.c_str());
	} catch (LuaIntf::LuaException& e) {
		std::cerr << Fmt("Failed to run '%1%', %2%\n")
			% sLuaInitFilePath % e.what();
	}
}

std::string LuaTest::GetLuaRootPath() const
{
	return "../scripts";
}

void LuaTest::RequestRpc(
	const std::string& sService,
	const std::string& sMethod,
	const std::string& sRequest,
	const LuaIntf::LuaRef& luaCallback) const
{
	RpcCallback cb;  // default empty callback
	if (!luaCallback)
		;
	else if (luaCallback.isFunction())
		cb = luaCallback.toValue<RpcCallback>();  // Todo: catch...
	else
		std::cout << "WARN: Lua callback in request is " << luaCallback.typeName()
			<< std::endl;

	m_pClient->PushRpcReq(sService, sMethod, sRequest, cb);
}

